package mysock

import (
	"encoding/binary"
	"net"
	"server_cluster/common/config"
//	"server_cluster/common/logger"
	"server_cluster/common/tools"
	"server_cluster/common/myepoll"
	"time"
	//	"syscall"
	//	"unsafe"
)

// head('|')+Command(uint32)+Session(uint64)+Code(uint16)+Ext(uint16)+Length(uint32)+Data(length) 服务器内部session切换为uid

const (
	OUTSIDE = 1
	INSIDE  = 2
)

const (
	HEAD_LEN = 21
)

type SockBuf struct {
	Command     uint32 // 命令号
	Session     uint64 // server session
	Code        uint16 // 错误码
	Ext         uint16 // 扩展字段（前端使用）
	Data_length uint32 // 数据长度
	Data        []byte // 数据
}

func Create() *SockBuf {
	tmp := &SockBuf{}
	tmp.Data = make([]byte, config.BUF_MAX)
	return tmp
}

func (mybuf *SockBuf) init() {
	mybuf.Command = 0
	mybuf.Session = 0
	mybuf.Code = 0
	mybuf.Ext = 0
	mybuf.Data_length = 0
	mybuf.Data[0] = 0
}

/*
func isSocketWritable(fd int) bool {
	var r, e uint32
	syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.FIONREAD), uintptr(unsafe.Pointer(&r)))
	err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR, &e)
	return r > 0 || err == nil
}*/

func isConnClose(err error) bool {

	if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
		// timeout ... connection is still alive
		return false
	}
	return true

	/*
		 switch err.(type) {
		 case net.Error:
			return err.(net.Error).Temporary() && err.(net.Error).Timeout()
		 default:
			return false
		 }*/
}

func sRead(conn net.Conn, buff []byte, length int) uint16 {
	if length < 0 || length > config.BUF_MAX {
		return config.BOUND
	}
	var site int
	max := 0

	for site = 0; site < length; {
		conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))
		n, err := conn.Read(buff[site:length])
		if err != nil && isConnClose(err) {
			//logger.Error("read socket %s is broken.err=%v", conn.RemoteAddr().String(), err)
			return config.FAIL
		}
		if n == 0 {
			if length == 1 {
				return config.TIMEOUT
			}
			max++
			if max > 30 {
				return config.INVALID
			}

		} else if n > 0 {
			site += n
		}
	}
	return config.OK
}

func sWrite(conn net.Conn, buff []byte, length int) uint16 {
	if length <= 0 {
		return config.BOUND
	}
	var site int

	for site = 0; site < length; {
		conn.SetWriteDeadline(time.Now().Add(10 * time.Millisecond))
		n, err := conn.Write(buff[site:length])
		if err != nil && isConnClose(err) {
			//logger.Error("write socket %s is broken.err=%v", conn.RemoteAddr().String(), err)
			return config.FAIL
		}
		if n > 0 {
			site += n
		}
	}
	return config.OK
}

func (mybuf *SockBuf) myRead(conn net.Conn, Type int8) uint16 {
	mybuf.init()
	var flag uint16
	sum := int32(0)
	site := uint32(0)
	for {
		flag = sRead(conn, mybuf.Data[site:], 1)
		if flag != config.OK {
			return flag
		}

		if mybuf.Data[site] == '|' {
			break
		}
		sum++
		if sum > config.BUF_MAX {
			//logger.Error("sum = %d", sum)
			return config.OVERRUN
		}
	}
	site++
	// command
	flag = sRead(conn, mybuf.Data[site:], 4)
	if flag != config.OK {
		return flag
	}
	mybuf.Command = binary.BigEndian.Uint32(mybuf.Data[site:])
	site += 4
	// log.Printf("command=%d\n",mybuf.Command)
	flag = sRead(conn, mybuf.Data[site:], 8)
	if flag != config.OK {
		return flag
	}
	mybuf.Session = binary.BigEndian.Uint64(mybuf.Data[site:])
	site += 8

	flag = sRead(conn, mybuf.Data[site:], 2)
	if flag != config.OK {
		return flag
	}
	mybuf.Code = binary.BigEndian.Uint16(mybuf.Data[site:])
	site += 2

	flag = sRead(conn, mybuf.Data[site:], 2)
	if flag != config.OK {
		return flag
	}
	mybuf.Ext = binary.BigEndian.Uint16(mybuf.Data[site:])
	site += 2

	flag = sRead(conn, mybuf.Data[site:], 4)
	if flag != config.OK {
		return flag
	}
	mybuf.Data_length = binary.BigEndian.Uint32(mybuf.Data[site:])
	site += 4

	if HEAD_LEN+mybuf.Data_length > config.BUF_MAX {
		//logger.Error("buf_length = %d", HEAD_LEN+mybuf.Data_length)
		return config.OVERRUN
	}
	//log.Printf("Socket Read:Command %d Session=%d Code=%d,Data_length = %d Ext=%d", mybuf.Command, mybuf.Session, mybuf.Code, mybuf.Data_length, mybuf.Ext)

	if mybuf.Data_length == 0 {
		return config.OK
	}
	flag = sRead(conn, mybuf.Data[site:], int(mybuf.Data_length))
	if flag != config.OK {
		return flag
	}

	if config.ENCRYPT_OPEN && Type == OUTSIDE {
		data := tools.AesDecrypt(mybuf.Data[site:mybuf.Data_length], int(mybuf.Session), true)
		mybuf.Data_length = uint32(len(data))
		if HEAD_LEN+mybuf.Data_length > config.BUF_MAX {
			return config.OVERRUN
		}
		var i uint32
		for i = 0; i < mybuf.Data_length; i++ {
			mybuf.Data[site+i] = data[i]
		}
	}
	//
	return config.OK
}

// read from out side
func (mybuf *SockBuf) ReadFromOutSide(conn net.Conn) uint16 {
	return mybuf.myRead(conn, OUTSIDE)
}

// read from in side
func (mybuf *SockBuf) ReadFrom(conn net.Conn) uint16 {
	return mybuf.myRead(conn, INSIDE)
}

// forward only by fd
func (mybuf *SockBuf) ForwardByFd(fd int32) uint16 {
	eps := &myepoll.Eps
	conn := eps.GetConnByFd(fd)
	if conn == nil {
		return config.NOTFOUND
	}
	//logger.Info("Socket forward:Data_length = %d", mybuf.Data_length)
	return sWrite(conn.Socket, mybuf.Data[:HEAD_LEN+mybuf.Data_length], int(HEAD_LEN+mybuf.Data_length))
}

// forward with session
func (mybuf *SockBuf) ForwardWithNewSession(fd int32, new_session uint64) uint16 {
	//logger.Info("Socket forward with session:%d", new_session)
	binary.BigEndian.PutUint64(mybuf.Data[5:], new_session)
	return mybuf.ForwardByFd(fd)
}

func (mybuf *SockBuf) WriteToServer(Type int8,Sid int16,data []byte) {
	eps := &myepoll.Eps
	fd := eps.GetFdByMap(Type,Sid)
	if fd < 0 {
		return
	}
	if mybuf.WriteToByFd(fd,data) != config.OK {
		eps.CleanServerConn(Type,Sid)
	}
}

func (mybuf *SockBuf) ServerForward(Type int8,Sid int16) {
	eps := &myepoll.Eps
	fd := eps.GetFdByMap(Type,Sid)
	if fd < 0 {
		return
	}
	if mybuf.ForwardByFd(fd) != config.OK {
		eps.CleanServerConn(Type,Sid)
	}
}

func (mybuf *SockBuf) ServerForwardWithNewSession(Type int8,Sid int16,new_session uint64) {
	eps := &myepoll.Eps
	fd := eps.GetFdByMap(Type,Sid)
	if fd < 0 {
		return
	}
	if mybuf.ForwardWithNewSession(fd,new_session) != config.OK {
		eps.CleanServerConn(Type,Sid)
	}
}

// 注意：buf_data 的数据长度不能超过(BUF_MAX-包头长度)
func (mybuf *SockBuf) myWrite(conn net.Conn, Type int8, buf_data []byte) uint16 {
	if config.ENCRYPT_OPEN && Type == OUTSIDE {
		buf_data = tools.AesEncrypt(buf_data, int(mybuf.Session), true)
	}
	mybuf.Data_length = uint32(len(buf_data))
	var site, i uint32

	site = 0
	mybuf.Data[site] = '|'
	site++
	binary.BigEndian.PutUint32(mybuf.Data[site:], mybuf.Command)
	site += 4
	binary.BigEndian.PutUint64(mybuf.Data[site:], mybuf.Session)
	site += 8
	binary.BigEndian.PutUint16(mybuf.Data[site:], mybuf.Code)
	site += 2
	binary.BigEndian.PutUint16(mybuf.Data[site:], mybuf.Ext)
	site += 2
	binary.BigEndian.PutUint32(mybuf.Data[site:], mybuf.Data_length)
	site += 4
	//

	if HEAD_LEN+mybuf.Data_length > config.BUF_MAX {
		return config.OVERRUN
	}

	for i = 0; i < mybuf.Data_length; i++ {
		mybuf.Data[site] = buf_data[i]
		site++
	}
	//log.Printf("Socket Write:Command %d Session=%d Code=%d,Data_length = %d Ext=%d [%s]", mybuf.Command, mybuf.Session, mybuf.Code, mybuf.Data_length, mybuf.Ext,conn.RemoteAddr())
	//
	return sWrite(conn, mybuf.Data[:site], int(site))
}

// write out side
func (mybuf *SockBuf) WriteOutByConn(conn net.Conn, buf_data []byte) uint16 {
	return mybuf.myWrite(conn, OUTSIDE, buf_data)
}

// write in side
func (mybuf *SockBuf) WriteToByConn(conn net.Conn, buf_data []byte) uint16 {
	return mybuf.myWrite(conn, INSIDE, buf_data)
}

// write out side by fd
func (mybuf *SockBuf) WriteOutByFd(fd int32, buf_data []byte) uint16 {
	eps := &myepoll.Eps
	conn := eps.GetConnByFd(fd)
	if conn == nil {
		return config.NOTFOUND
	}
	return mybuf.WriteOutByConn(conn.Socket, buf_data)
}

// write in side by fd
func (mybuf *SockBuf) WriteToByFd(fd int32, buf_data []byte) uint16 {
	eps := &myepoll.Eps
	conn := eps.GetConnByFd(fd)
	if conn == nil {
		return config.NOTFOUND
	}
	return mybuf.WriteToByConn(conn.Socket, buf_data)
}
